package org.ccay.excel.importer.validate.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import ognl.DefaultTypeConverter;
import ognl.Ognl;
import ognl.OgnlException;
import ognl.TypeConverter;

import org.springframework.util.ClassUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import org.ccay.core.log.ILogger;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.util.DomUtil;
import org.ccay.core.util.StreamUtil;
import org.ccay.core.util.StringUtil;
import org.ccay.excel.exception.ExcelApplicationException;
import org.ccay.excel.importer.validate.IValidator;
import org.ccay.excel.importer.validate.ValidatorDef;
import org.ccay.excel.meta.ExcelMeta;
import org.ccay.excel.meta.IExcelMetaManager;
import org.ccay.excel.meta.SheetMeta;
import org.ccay.excel.meta.ValidatorMeta;

/**
 * Excel验证器管理。
 * 	
 * @author chaowangbang
 * @since Mar 16, 2012
 */
public abstract class ExcelValidatorManager {
	
	/** 日志记录器 **/
	private static ILogger logger = CcayLoggerFactory.getLogger(ExcelValidatorManager.class);
	
	/**
	 * 系统中定义的validator列表。
	 */
	private static Map<String, ValidatorDef> validatorDefinitions = new HashMap<String, ValidatorDef>();
	
//	/**
//	 * 列对应的验证器列表缓存
//	 * key: excelType.sheetName.colName
//	 */
//	private static Map<String, List<IValidator>> validatorCache = new WeakHashMap<String, List<IValidator>>();
	
	private static ConcurrentHashMap<String, Object> expressions = new ConcurrentHashMap<String, Object>();
    
    private static boolean enableExpressionCache = true;
	
	/**
	 * Excel元数据管理
	 */
	private static IExcelMetaManager excelMetaManager;
	
	
	private static boolean initialized = false;
	
	/**
	 * 初始化。
	 * 解析excel.validators.xml文件获取其中的validator定义
	 */
	static void initialize() {
		InputStream stream = null;
		try {
			 stream = ExcelValidatorManager.class.getResourceAsStream("/config/ccay.excel.validators.xml");
			 
			if(stream == null) {
				logger.warn("Could not found excel.validators.xml file.");
				return;
			}
			
			InputSource inputSource = new InputSource(stream);
			Document doc = DomUtil.loadDocument(inputSource);
			registerSysValidators(doc);
		} catch (IOException ioe) {
			logger.error(ioe);
		} finally {
			StreamUtil.closeStreams(stream);
		}
	}
	
	/**
	 * 注册系统验证器。
	 * 
	 * @param doc
	 */
	private static void registerSysValidators(Document doc) {
		List<Element> validatorElems = DomUtil.getChildElementsByTagName(doc.getDocumentElement(), "validator");
		for(Iterator<Element> iter = validatorElems.iterator(); iter.hasNext(); ) {
			Element validatorElem = iter.next();
			String name = validatorElem.getAttribute("name");
			String clazz = validatorElem.getAttribute("class");
			String messageKey = validatorElem.getAttribute("messageKey");
			if(!StringUtil.isNullOrEmpty(name) && !StringUtil.isNullOrEmpty(clazz)) {
				ValidatorDef validatorDef = new ValidatorDef(name);
				validatorDef.setClassName(clazz);
				validatorDef.setMessageKey(messageKey);
				
				registerValidator(validatorDef);// 注册
			}
		}
	}
	
	/** 设置Excel元数据管理器	
	 * @author chaowangbang
	 * @since Mar 20, 2012
	 * @param metaManager
	 */
	public static void setExcelMetaManager(IExcelMetaManager metaManager) {
		excelMetaManager = metaManager;
	}
	
	/** 获取某列的验证器列表	
	 * @author chaowangbang
	 * @since Mar 20, 2012
	 * @param excelType
	 * @param sheetName
	 * @param colName
	 * @return
	 * @throws ExcelApplicationException
	 */
	public static List<IValidator> getValidators(ExcelMeta excelMeta, String sheetName, String colName) throws ExcelApplicationException {
		// 创建一个key
//		String cacheKey = generateCacheKey(excelMeta.getType(), sheetName, colName);
		
		// 尝试从缓存中获取
//		List<IValidator> validators = validatorCache.get(cacheKey);
//		if(validators != null) {
//			return validators;
//		}
		
//		Assert.notNull(excelMetaManager);
		
//		synchronized (validatorCache) {
//			validators = validatorCache.get(cacheKey);
//			if(validators != null) {
//				return validators;
//			}
			
			if(!initialized) {
				initialized = true;
				initialize();
			}
			
			List<ValidatorMeta> validatorMetas = excelMeta.getValidatorMetas(sheetName, colName);//getValidatorMetas(excelType, sheetName, colName);
			
			List<IValidator> validators = buildValidators(validatorMetas);
			
//			validatorCache.put(cacheKey, validators);
//		}
	
		return validators;
	}
	
	/**
	 * 获取当前sheet列验证器列表	
	 * @author chaowangbang
	 * @since 2013-6-26
	 * @param sheetMeta
	 * @param colName
	 * @return
	 * @throws ExcelApplicationException
	 */
	public static List<IValidator> getValidators(SheetMeta sheetMeta, String colName) throws ExcelApplicationException{
		
		if(!initialized) {
			initialized = true;
			initialize();
		}
		
		List<ValidatorMeta> validatorMetas= sheetMeta.getValidatorMetas(colName);
		
		List<IValidator> validators=buildValidators(validatorMetas);
		
		return validators;
	}
	
	/**
	 * 获取validator的定义列表。
	 * 
	 * @param excelType
	 * @param sheetName
	 * @param colName
	 * @return
	
	private static List<ValidatorMeta> getValidatorMetas(String excelType, String sheetName, String colName) {
		ExcelMeta excelMeta = null;
		try {
			excelMeta = excelMetaManager.getExcelMeta(excelType);
		} catch (ApplicationException ex) {
			logger.error("Could not found excel configuration file for "+excelType);
			return Collections.EMPTY_LIST;
		}
		
		return excelMeta.getValidatorMetas(sheetName, colName);
	}*/
	
	/**
	 * 构造IValidator实例。
	 * 
	 * @param validatorMetas
	 * @return
	 * @throws ExcelApplicationException
	 */
	private static List<IValidator> buildValidators(List<ValidatorMeta> validatorMetas) throws ExcelApplicationException {
		List<IValidator> validators = new ArrayList<IValidator>(validatorMetas.size());
		
		Iterator<ValidatorMeta> iter = validatorMetas.iterator();
		while(iter.hasNext()) {
			ValidatorMeta validatorMeta = iter.next();
			IValidator validator = buildValidator(validatorMeta);
			if(validator != null) {
				validators.add(validator);
			}
		}
		
		return validators;
	}
	
	/** 注册一个验证器的定义	
	 * @author chaowangbang
	 * @since Mar 20, 2012
	 * @param validatorDef
	 */
	public static void registerValidator(ValidatorDef validatorDef) {
		if(!initialized) {
			initialized = true;
			initialize();
		}
		
		synchronized (validatorDefinitions) {
			if(validatorDefinitions.containsKey(validatorDef.getName())) {
				logger.warn("The validator with the same name '"+validatorDef.getName()+"' has already exist.");
				return;
			}
			
			validatorDefinitions.put(validatorDef.getName(), validatorDef);
		}
	}
	
	/** 生成缓存主键	
	 * @author chaowangbang
	 * @since Mar 20, 2012
	 * @param excelType
	 * @param sheetName
	 * @param colName
	 * @return
	 */
	private static String generateCacheKey(String excelType, String sheetName, String colName) {
		return excelType+"."+sheetName+"."+colName;
	}
	
	/** 获取指定名称的验证器定义	
	 * @author chaowangbang
	 * @since Mar 22, 2012
	 * @param name
	 * @return
	 */
	private static ValidatorDef getValidatorDefine(String name) {
		return validatorDefinitions.get(name);
	}
	
	/** 根据验证器的配置，构造并初始化一个验证器实例。	
	 * @author chaowangbang
	 * @since Mar 22, 2012
	 * @param meta
	 * @return
	 * @throws ExcelApplicationException
	 */
	private static IValidator buildValidator(ValidatorMeta meta) throws ExcelApplicationException {
		String type = meta.getType();
		ValidatorDef def = getValidatorDefine(type);
		if(def == null) {
			logger.warn("Can't find validator definition [type: "+type+"].");
			return null;
		}
		
		// 实例化并初始化validator
		try {
			Class<?> clazz = ClassUtils.forName(def.getClassName(), ExcelValidatorManager.class.getClassLoader());
			Object obj = clazz.newInstance();
			if(!(obj instanceof IValidator)) {
				logger.warn("The validator: "+def.getClassName()+" is illegal, it must implement "+IValidator.class.getName()+" interface.");
				return null;
			}
		
			IValidator validator = (IValidator)obj;
			initialValidator(validator, meta, def);

			return validator;
			
		} catch (Exception t) {
			// 异常处理：实例化validator({0})时发生错误。
			// 仅仅只是记录异常，不能因为validator实例化失败而影响整个excel的导入
			logger.error(t);
			//throw new ExcelImportException(ExcelImportConstants.VALIDATOR_INSTANCE_FAIL, t);
		}
		
		return null; 
	}
	
	/**
	 * 初始化validator.
	 * 
	 * @param validator
	 * @param meta
	 * @param def
	 * @throws OgnlException
	 */
	private static void initialValidator(IValidator validator, ValidatorMeta meta, ValidatorDef def) throws OgnlException {
		validator.setType(meta.getType());
		
		if(!meta.getParams().isEmpty()) {
			Map ctx = Ognl.createDefaultContext(validator);
			setProperties(meta.getParams(), validator, ctx);
		}
		
		/**
		 * 用户未配置消息主键时使用验证器定义中配置的默认消息主键
		 */
		if(StringUtil.isNullOrEmpty(validator.getMessageKey())) {
			validator.setMessageKey(def.getMessageKey());
		}
	}
	
	/** 设置属性
	 * @author chaowangbang
	 * @since Mar 20, 2012
	 * @param props
	 * @param o
	 * @param context
	 * @throws OgnlException
	 */
	public static void setProperties(Map<String, ?> props, Object o, Map<String, Object> context) throws OgnlException {
        if (props == null) {
            return;
        }

        TypeConverter typeConverter = new DefaultTypeConverter();
        Ognl.setTypeConverter(context, typeConverter);

        Object oldRoot = Ognl.getRoot(context);
        Ognl.setRoot(context, o);

        for (Map.Entry<String, ?> entry : props.entrySet()) {
            String expression = entry.getKey();
            Ognl.setValue(compile(expression), context, o, entry.getValue());
        }

        Ognl.setRoot(context, oldRoot);
    }
	
    /** 编译EL表达式	
     * @author chaowangbang
     * @since Mar 20, 2012
     * @param expression
     * @return
     * @throws OgnlException
     */
    private static Object compile(String expression) throws OgnlException {
        if (enableExpressionCache) {
            Object o = expressions.get(expression);
            if (o == null) {
                o = Ognl.parseExpression(expression);
                expressions.put(expression, o);
            }
            return o;
        } else {
            return Ognl.parseExpression(expression);
        }
    }

}
